import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class RC4 {

		private int[] S = new int[256];
		private char[] CipherKey;
		private KeyStore Keystore;
		
		public RC4() {
			
			Security.addProvider(new BouncyCastleProvider());
			CreateKeyStore("password");
			CreateSBox();
			GenerateSBox();
		}
		
		private void CreateKeyStore(String passphrase) {
			
			char[] password = passphrase.toCharArray();
			
			try {
				Keystore = KeyStore.getInstance("UBER");
				Keystore.load(null,password);
				Keystore.setKeyEntry("key", GenerateSecretKey("maslo"), password ,null);
			} 
			catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		private SecretKey GenerateSecretKey(String key) {
			
			SecretKeyFactory factory;
			SecretKey sKey = null;
			
			try {
				factory = SecretKeyFactory.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC", "BC");
				KeySpec keySpec = new PBEKeySpec(key.toCharArray());
				sKey = factory.generateSecret(keySpec);
			} 
			catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException e) {
				e.printStackTrace();
			}
			
			return sKey;
		}
		
		private char[] GetKeyFromKeystore(String passphrase)  {
			
			char[] password = passphrase.toCharArray();
			char[] charKey = null;
			
			byte[] byteKey = null;
			
			try {
				byteKey = Keystore.getKey("key", password).getEncoded();
			} 
			catch (UnrecoverableKeyException | KeyStoreException
					| NoSuchAlgorithmException e) {
				e.printStackTrace();
			}
			charKey = String.valueOf(byteKey).toCharArray();

			return charKey;
		}
		
		private void CreateSBox() {
			
			for (int i=0; i<255; i++)
					S[i]= i;
		}
		
		private void GenerateSBox() {

			int j = 0;
			//char[] key = Key.toCharArray(); 
			char[] key = GetKeyFromKeystore("password");
			int d = key.length;
			
			for (int i=0; i<255; i++) {
				j = (j + S[i] + key[ i % d ]) % 256;
				
				int temp = S[i];
				S[i] = S[j];
				S[j] = temp;
			}
		}
		
		private void GenerateCipherKey(String mess) {
			int i = 0;
			int j = 0;
			
			char[] charMess = mess.toCharArray();
			int d = charMess.length;
			
			CipherKey = new char[d];
			
			for (int a=0; a<d; a++) {
				i = (i + 1) % 256;
				j = (j + S[i]) % 256;
				
				int temp = S[i];
				S[i] = S[j];
				S[j] = temp;
				
				CipherKey[a] = (char) (S[(S[i] + S[j]) % 256]);
			}
		}
		
		private char[] EncryptMessageWithCipherKey(String mess) {
			
			char[] charMess = mess.toCharArray();
			int d = charMess.length;
			
			//System.out.println("mess = "+charMess);
			
			char[] resultMess = new char[d];
			
			for (int a=0; a<d; a++) {
				resultMess[a] = (char) (charMess[a] ^ CipherKey[a]);
			}
			
			return resultMess;
		}
		
		public String EncryptMessage(String mess) {
			
			try {
				GenerateCipherKey(mess);
				char[] EncryptedMessage = EncryptMessageWithCipherKey(mess);
				
				return String.valueOf(EncryptedMessage);
			} 
			catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}
		
		public String DecryptMessage(String mess) {
			
			char[] EncryptedMessage = EncryptMessageWithCipherKey(mess);
			
			return String.valueOf(EncryptedMessage);
		}
}
